package com.foxitcloud.webpdf;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;


import com.foxitcloud.util.JsonUtil;
import com.foxitcloud.webpdf.cache.ICacheStoreProvider;
import com.foxitcloud.webpdf.calldll.FXPDFError;
import com.foxitcloud.webpdf.common.CacheDescriptionHandle;
import com.foxitcloud.webpdf.common.Config;
import com.foxitcloud.webpdf.common.ConfigConstans;
import com.foxitcloud.webpdf.common.DataRetrievalStatus;
import com.foxitcloud.webpdf.common.ImgSignatureData;
import com.foxitcloud.webpdf.common.OperationData;
import com.foxitcloud.webpdf.common.OperationError;
import com.foxitcloud.webpdf.common.OperationType;
import com.foxitcloud.webpdf.common.TextSignatureData;
import com.foxitcloud.webpdf.entity.Signature;
import com.foxitcloud.webpdf.exception.CacheException;
import com.foxitcloud.webpdf.parameter.PdfOperationParameter;
import com.foxitcloud.webpdf.provider.IWebReaderStoreProvider;
import com.foxitcloud.webpdf.utils.AESUtils;
import com.foxitcloud.webpdf.utils.FXFileUtils;
import com.foxitcloud.webpdf.utils.FXStringUtils;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;


@Service
@Scope("prototype")
public class WebPDFService  {
	private static final Logger logger = LoggerFactory.getLogger(WebPDFService.class);
	
	class GetImageError {
		public static final int ERROR_PAGE_DISPLAY_LIMIT = 1;
		public static final int ERROR_CREATE_IMG_FAILED = 2;
		public static final int ERROR_CREATE_IMG_INPROGRESS = 3;
	}			
	
	
	@Autowired
	@Qualifier("cacheStoreProvider")
	private ICacheStoreProvider cacheStoreProvider;
	
	@Autowired
	@Qualifier("iWebReaderStoreProvider")
	private IWebReaderStoreProvider iWebReaderStoreProvider;
	
	private String cacheRootPath = Config.getValue(ConfigConstans.FOXIT_CACHE_ROOT_PATH_KEY, "");
	
	public WebPDFService() {
		if (!this.cacheRootPath.endsWith("/") && !this.cacheRootPath.endsWith("\\")){
			this.cacheRootPath += "/";
		}
	}	

	public Map<String, Object> prepare(String s3Key,boolean formMode, String pdfPassword) {
		String cacheDescription = CacheDescriptionHandle
				.getManifestInfoCacheDescription(formMode, false);
		String manifestPath = cacheStoreProvider.getCacheItemPath(s3Key, cacheDescription);
		HashMap<String, Object> retmap = new HashMap<String, Object>();
		int status = 0;
		if (new File(manifestPath).exists()) {
			HashMap<String, Object> inputMap = new HashMap<String, Object>();
	        inputMap.put("s3Key", s3Key);
	        inputMap.put("formMode", formMode);
	        inputMap.put("pdfPassword", pdfPassword);		    
		}
		
		retmap.put("status", status);
		logger.warn("prepare file:" + s3Key + "; status:" + String.valueOf(status));
		return retmap;
	}	
	
	
	public Map<String, Object> getSummaryInfo(HttpServletResponse response,String s3Key, boolean formMode,boolean isCheckPsd,String pdfPassword) {
		String docManifestInfo = "";
		String cacheDescription = CacheDescriptionHandle
				.getManifestInfoCacheDescription(formMode, false);
		
		logger.info(cacheDescription);
		try {
			InputStream pagesInfoStream = cacheStoreProvider.getCachedStream(s3Key,
					cacheDescription, true);
					
			if (pagesInfoStream == null) {
				HashMap<String, Object> inputMap = new HashMap<String, Object>();
		        inputMap.put("s3Key", s3Key);
		        inputMap.put("formMode", formMode);
		        inputMap.put("pdfPassword", pdfPassword);
		        inputMap.put("onlyCreateDocInfo", true);   
				pagesInfoStream = cacheStoreProvider.getCachedStream(s3Key,
						cacheDescription, true);
			} 
			
			if (pagesInfoStream == null) {
				response.setStatus(HttpServletResponse.SC_NOT_FOUND);
				return null;
			} else {
				boolean isStreamClosed = false;
				try {
					String pdfPasswordFromCache = cacheStoreProvider
							.getPDFPassword(s3Key);
					if (pdfPasswordFromCache == null) {
						logger.warn("Check PDF password from cache failed, id:"
								+ s3Key);
						this.setErrorResponse(response, DataRetrievalStatus.UnknownError,
								FXPDFError.ERROR_UNKNOWN);
						return null;
					} else {
						if (pdfPasswordFromCache.length() > 0) {
							pdfPasswordFromCache = AESUtils.decrypt(
									pdfPasswordFromCache,
									ConfigConstans.FOXIT_DOC_PASSWORD_DEFAULTPASS);
							if (!pdfPassword.equals(pdfPasswordFromCache)) {
								logger.warn("Invalid password.");
								this.setErrorResponse(response,
										DataRetrievalStatus.UnknownError,
										FXPDFError.ERROR_PDFPARSE_PASSWORD);
								return null;
							}
						}
	
						docManifestInfo = FXStringUtils.inputStreamTOString(
								pagesInfoStream, "utf-8");
						isStreamClosed = true;
						if (docManifestInfo == null) {
							logger.warn("Read manifest information from stream failed.");
							docManifestInfo = "";
						} else {
							iWebReaderStoreProvider.setPDFPasswordEncrypted(s3Key, false);
						}
					}
				} finally {
					if (!isStreamClosed) {
						pagesInfoStream.close();
					}
				}
			}
		} catch (Exception e) {
			logger.warn(e.getMessage());
			try {
				this.setErrorResponse(response, DataRetrievalStatus.UnknownError,
						FXPDFError.ERROR_UNKNOWN);
			} catch (IOException e1) {
			}
			return null;
		}
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("docinfo", docManifestInfo);
		map.put("error", 0);
		
		response.addHeader("P3P", "CP=IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT");
		response.setHeader("Pragma", "no-cache");
		response.setDateHeader("Expires", -1);
		response.addHeader("Cache-Control", "no-cache");
		response.addHeader("Cache-Control", "no-store");
		response.addHeader("Cache-Control", "must-revalidate");
		return map;
	}
		
	public Map<String, Object> getAnnotData(HttpServletRequest request,HttpServletResponse response,String s3Key, boolean formMode) {
		String description = CacheDescriptionHandle.getAnnnosCacheDescription(false);	
		if (!this.isManifestDataAccessed(response, s3Key)) {
			return null;
		}
		
		String etag = checkNoneMathch(response, request, s3Key, description);
		if (etag == null) {
			return null;
		}
		
		String data = "";
		InputStream stream = null;
		try {
			stream = cacheStoreProvider.getCachedStream(
					s3Key, description, false);
			if (stream == null) {
				response.setStatus(HttpServletResponse.SC_NOT_FOUND);
				return null;
			} else {
				data = FXStringUtils.inputStreamTOString(stream, "utf-8");
				stream = null;
				if (data == null) {
					logger.warn("Read annotation information from stream failed.");
					data = "";
				}
			}
		} catch (Exception ex) {
			logger.warn(ex.getMessage());
			return null;
		} finally {
			if (stream != null) {
				try {
					stream.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("error", 0);
		map.put("data", data);
		Date now = new Date();
		//设置Expires的话，在expires之前客户端不会发生etag检测
		response.setDateHeader("Last-Modified", now.getTime());
		response.setDateHeader("Expires", now.getTime() + 1200000); 
		response.setHeader("Cache-Control", "public");
		response.setHeader("Pragma", "Pragma");
		response.setHeader("ETag", etag);
		return map;
	}
	
	
	public Map<String, Object> getBookMarkData(HttpServletRequest request,HttpServletResponse response,String s3Key, boolean formMode) {
		String description = CacheDescriptionHandle.getBookmarksCacheDescription(false);	
		if (!this.isManifestDataAccessed(response, s3Key)) {
			return null;
		}
		
		String etag = checkNoneMathch(response, request, s3Key, description);
		if (etag == null) {
			return null;
		}
		
		String data = "";
		InputStream stream = null;
		try {
			stream = cacheStoreProvider.getCachedStream(
					s3Key, description, false);
			if (stream == null) {
				response.setStatus(HttpServletResponse.SC_NOT_FOUND);
				return null;
			} else {
				data = FXStringUtils.inputStreamTOString(stream, "utf-8");
				stream = null;
				if (data == null) {
					logger.warn("Read bookmark information from stream failed.");
					data = "";
				}
			}
		} catch (Exception ex) {
			logger.warn(ex.getMessage());
			return null;
		} finally {
			if (stream != null) {
				try {
					stream.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("error", 0);
		map.put("BookmarkInfo", data);
		Date now = new Date();
		//设置Expires的话，在expires之前客户端不会发生etag检测
		response.setDateHeader("Last-Modified", now.getTime());
		response.setDateHeader("Expires", now.getTime() + 1200000); 
		response.setHeader("Cache-Control", "public");
		response.setHeader("Pragma", "Pragma");
		response.setHeader("ETag", etag);
		return map;
	}
	
	/**
	 * 获取表单图片
	 */	
	public void getFormImg(String s3Key,int pageIndex,HttpServletResponse response)throws IOException{
		String description = CacheDescriptionHandle.getFormImageCacheDescription(pageIndex,false);
		InputStream formBGImgStream = cacheStoreProvider.getCachedStream(s3Key,description,false);
		try {
				if (formBGImgStream == null &&
						readerPage(s3Key, true, pageIndex, null)) {
					formBGImgStream = cacheStoreProvider.getCachedStream(s3Key,description,false);
				}
			    if (formBGImgStream == null) {
			    	response.setStatus(HttpServletResponse.SC_NOT_FOUND);
			    	return;
			    }
				byte[] imgData = FXFileUtils.readToByteArray(formBGImgStream);
				response.setContentType("image/png");
				Date now = new Date();
				response.setDateHeader("Expires", now.getTime() + 1200000); // 20
				response.setHeader("Cache-Control", "public");
				response.setHeader("Pragma", "Pragma");
				response.setContentLength(imgData.length);
				OutputStream out = response.getOutputStream();
				out.write(imgData);
				out.close();
		} catch(Exception e){
			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
			logger.warn("Access to form a picture of failure");
			return;
		} finally {
			if (formBGImgStream != null)
				formBGImgStream.close();
		}
	};
	
	/**
	 * 获取表单数据
	 */	
	public void getFormPageXml(String s3Key,String pageIndex,HttpServletResponse response)throws IOException{
		//分割 pageIndex
		pageIndex  = StringEscapeUtils.unescapeJava(pageIndex).trim();
		boolean bGetRequiredFieldXml = pageIndex.equalsIgnoreCase("RequiredField");
		ArrayList<Integer> pageIndexList = new ArrayList<Integer>();
		if (!bGetRequiredFieldXml) {
			String[] pageIndexArr = pageIndex.split(";");
			for (String strPageIndex : pageIndexArr) {
				if (!StringUtils.isEmpty(strPageIndex)) {
					pageIndexList.add(Integer.parseInt(strPageIndex));
				}
			}
		}
		
		//验证 是否访问过 mianfest 接口
		if (!this.isManifestDataAccessed(response, s3Key)) {
			return;
		}
		
		String formXmlInfo = "";
		Document docPages;
		try {
			docPages = DocumentHelper
					.parseText("<?xml version=\"1.0\" encoding=\"utf-8\" ?><Pages></Pages>");
		} catch (DocumentException e) {
			logger.warn(e.toString());
			return;
		}
		
		if (bGetRequiredFieldXml) {
			InputStream requiredfieldStream = cacheStoreProvider.getCachedStream(
					s3Key, "form_requiredfield_xmldata", false);
			if (requiredfieldStream == null) {
				try {
					//获取页面总是
					String cacheDescriptionManifest = CacheDescriptionHandle
							.getManifestInfoCacheDescription(true, false);
					InputStream pagesInfoStream = cacheStoreProvider.getCachedStream(
							s3Key, cacheDescriptionManifest, true);
					if (pagesInfoStream == null) {
						return;
					}
					String docManifestInfo = FXStringUtils.inputStreamTOString(
							pagesInfoStream, "utf-8");
					pagesInfoStream.close();
					JsonParser jsonParser = new JsonParser();
					JsonElement jsonElement = jsonParser.parse(docManifestInfo);
					int count = jsonElement.getAsJsonObject().get("PageCount").getAsInt();
					for (int i = 0; i < count; i++) {
						int pageNo = i;
						String description = CacheDescriptionHandle
								.getFormXmlInfoCacheDescription(pageNo,false);
						InputStream formXmlStream = cacheStoreProvider.getCachedStream(
								s3Key, description, false);
						try {
							if (formXmlStream != null) {
								String pageFormXmlInfo = FXStringUtils.inputStreamTOString(
										formXmlStream, "utf-8");
								this.addRequiredFieldXmlInfoToXmlDoc(docPages, pageFormXmlInfo,
										pageNo);
							}
						} catch (Exception ex) {
							logger.warn(ex.getMessage());
							return;
						} finally {
							if (formXmlStream != null)
								formXmlStream.close();
						}
					}
					formXmlInfo = docPages.asXML();
					if (!FXStringUtils.IsNullOrEmpty(formXmlInfo)) {
						try{
							cacheStoreProvider.addItemToCache(s3Key, "form_requiredfield_xmldata", 
									formXmlInfo.getBytes("UTF-8"), "", false);
						} catch (Exception ex) {}
					}
				} catch (Exception ex) {}
			} else {
				formXmlInfo = FXStringUtils.inputStreamTOString(
						requiredfieldStream, "UTF-8");
			}
		} else {
			for (int i = 0; i < pageIndexList.size(); i++) {
				int currentIndex = pageIndexList.get(i);
				String description = CacheDescriptionHandle.getFormXmlInfoCacheDescription(currentIndex,false);
				InputStream formXmlStream = cacheStoreProvider.getCachedStream(s3Key,description,false);
				try {
					if (formXmlStream == null &&
							readerPage(s3Key, true, pageIndexList.size() == 1 ? currentIndex : -1, pageIndex)) {
						formXmlStream = cacheStoreProvider.getCachedStream(s3Key,description,false);
					}
					if (formXmlStream == null) {
						response.setStatus(HttpServletResponse.SC_NOT_FOUND);
						return;
					}
					String pageFormXmlInfo = FXStringUtils.inputStreamTOString(
							formXmlStream, "UTF-8");
					this.addXmlInfoToXmlDoc(docPages, pageFormXmlInfo, currentIndex);
				} catch (DocumentException e) {
					logger.warn(e.toString());
					response.setStatus(HttpServletResponse.SC_NOT_FOUND);
					return;
				} finally{
					if (formXmlStream != null)
						formXmlStream.close();
				}
			}
			formXmlInfo = docPages.asXML();
		}
		
		response.setContentType("text/xml");
		Date now = new Date();
		response.setDateHeader("Expires", now.getTime() + 1200000);
		response.setHeader("Cache-Control", "public");
		response.setHeader("Pragma", "Pragma");
		byte[] buf = formXmlInfo.getBytes("UTF-8");
		response.setContentLength(buf.length);
		OutputStream out = response.getOutputStream();
		try {
			out.write(buf, 0, buf.length);
		} catch (Exception ex) {
			logger.warn(ex.toString());
		} finally {
			out.close();
		}
	};
	
	/**
	 * 获取文本信息
	 * @throws IOException 
	 */	
	public Map<String,Object> getPageTextData(String s3Key,int pageIndex,boolean formMode,HttpServletResponse response,HttpServletRequest request) throws IOException{
		String description = CacheDescriptionHandle
				.getPageTextCacheDescription(pageIndex,false);
		//验证 是否访问过 mianfest 接口
		if (!this.isManifestDataAccessed(response, s3Key)) {
			return null;
		}
		
		String etag = checkNoneMathch(response, request, s3Key, description);
		if (etag == null) {
			return null;
		}
		
		String textPageInfo = "";
		InputStream textStream = cacheStoreProvider.getCachedStream(s3Key, description, false);
		try {
			if (textStream == null && readerPage(s3Key, formMode, pageIndex, null)) {
				textStream = cacheStoreProvider.getCachedStream(s3Key, description, false);
			}
			if (textStream == null) {
				response.setStatus(HttpServletResponse.SC_NOT_FOUND);
				return null;
			}
			textPageInfo = FXStringUtils.inputStreamTOString(textStream, "utf-8");
			if (textPageInfo == null) {
				logger.warn("Read text information from stream failed.");
				textPageInfo = "";
			}
			textStream = null;
		} catch (Exception e) {
			logger.warn(e.getMessage());
			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
			return null;
		} finally{
			if (textStream != null)
				textStream.close();
		}
		
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("error", FXPDFError.ERROR_SUCCESS);
		map.put("TextPageData", textPageInfo);
		Date now = new Date();
		response.setDateHeader("Last-Modified", now.getTime());
		response.setDateHeader("Expires", now.getTime() + 1200000);
		response.setHeader("Cache-Control", "public");
		response.setHeader("Pragma", "Pragma");
		response.addHeader("ETag", etag);
		return map;
	}
		
	public void getImageData(HttpServletResponse response,HttpServletRequest request,String s3Key, 
			int index, boolean isFormAndFormMode, boolean formMode, String zoom) {
		if (!this.isManifestDataAccessed(response, s3Key)) {
			return;
		}
		
		float fzoom = -1;
		if (zoom.equals("thumb")){
			fzoom = -1;
		}else{
			fzoom = Float.parseFloat(zoom) / 100;
		}
		String description = CacheDescriptionHandle.getPageImageCacheDescription(index, fzoom, 						
				(fzoom == -1.0f) ? false : isFormAndFormMode, false);
		
		String etag = checkNoneMathch(response, request, s3Key, description);
		if (etag == null) {
			return;
		}
		
		InputStream imageStream = cacheStoreProvider.getCachedStream(
				s3Key, description, false);
		try {
			if (imageStream == null && readerPage(s3Key, formMode, index, null)) {
				imageStream = cacheStoreProvider.getCachedStream(
						s3Key, description, false);
			}
			if (imageStream == null) {
				this.setErrorResponse(response, HttpServletResponse.SC_ACCEPTED,
						GetImageError.ERROR_CREATE_IMG_FAILED);
				return;
			}
			byte[] imgData = FXFileUtils.readToByteArray(imageStream);
			imageStream = null;
			if (imgData == null) {
				logger.warn("Read image data from stream failed.");
				this.setErrorResponse(response, HttpServletResponse.SC_ACCEPTED,
						GetImageError.ERROR_CREATE_IMG_FAILED);
				return;
			}
			
			response.setContentType("image/png");
			Date now = new Date();
			//cache for 20 mintues
			response.setDateHeader("Expires", now.getTime() + 86400000);
			response.setHeader("Cache-Control", "public");
			response.setHeader("Pragma", "Pragma");
			response.addHeader("ETag", etag);
			response.setContentLength(imgData.length);
			ServletOutputStream out = response.getOutputStream();
			out.write(imgData);
			out.flush();
			out.close();
		} catch (Exception ex) {
			logger.warn(ex.getMessage());
			try {
				this.setErrorResponse(response, HttpServletResponse.SC_ACCEPTED,
						GetImageError.ERROR_CREATE_IMG_FAILED);
			} catch (IOException e) {}
			return;
		} finally {
			if (imageStream != null) {
				try {
					imageStream.close();
				} catch (IOException e) {}
			}
		}
	}
	
	
	
	
	public Map<String, Object> pdfOperation(PdfOperationParameter pdfOperationParameter) {
		Map<String, Object> retMap = new HashMap<String, Object>();
		try {
			int operationType = pdfOperationParameter.getType();
			switch(operationType){
				case OperationType.OperationType_Save:
				{
					retMap = SaveOperation(pdfOperationParameter);
				}
				break;
			}
		} catch (Exception e) {
			logger.warn(e.getMessage());
			retMap.put("error", OperationError.ERROR_UNKNOWN);
		}
		
		return retMap;
	}
		
	
	public void getSignatureImgStream(HttpServletResponse response, String path){	
		try {
			String rootCachePath = this.cacheRootPath;
			String url = rootCachePath + "signature/" + path;
			byte[] imgData = FXFileUtils.readToByteArray(url);
			if (imgData == null) {
				logger.warn("Read image data from stream failed.");
				this.setErrorResponse(response, HttpServletResponse.SC_ACCEPTED,
						GetImageError.ERROR_CREATE_IMG_FAILED);
				return;
			}
			response.setContentType("image/png");
			Date now = new Date();
			response.setDateHeader("Expires", now.getTime() + 86400000);
			response.setHeader("Cache-Control", "public");
			response.setHeader("Pragma", "Pragma");
			response.setContentLength(imgData.length);
			ServletOutputStream out = response.getOutputStream();
			out.write(imgData);
			out.flush();
			out.close();
		} catch (Exception e) {
			logger.error(e.getMessage());
			try {
				this.setErrorResponse(response, HttpServletResponse.SC_ACCEPTED,
						GetImageError.ERROR_CREATE_IMG_FAILED);
			} catch (IOException e1) {}
		}
	}	
	
	
	private Map<String, Object> SaveOperation(PdfOperationParameter pdfOperationParameter) throws Exception {
		Map<String, Object> retMap = new HashMap<String, Object>();
		boolean isFormMode = pdfOperationParameter.isFormMode();
		String signatureData = pdfOperationParameter.getSignatureData();
		String signatureDocData = pdfOperationParameter.getSignatureDocData();
		String signatureDelData = pdfOperationParameter.getSignatureDelData();
		String formXmlData = pdfOperationParameter.getFormXmlData();
		String annotData = pdfOperationParameter.getAnnotData();
		OperationData operationData = new OperationData();
		
		logger.warn("signatureData");
		logger.warn("===========================================================");
		logger.warn(signatureData);
		logger.warn("\n===========================================================");
		logger.warn("signatureDocData");
		logger.warn("===========================================================");
		logger.warn(signatureDocData);
		logger.warn("\n===========================================================");
		logger.warn("signatureDelData");
		logger.warn("===========================================================");
		logger.warn(signatureDelData);
		logger.warn("\n===========================================================");
		logger.warn("annotData");
		logger.warn("===========================================================");
		logger.warn(annotData);
		logger.warn("\n===========================================================");
		logger.warn("formXmlData");
		logger.warn("===========================================================");
		logger.warn(formXmlData);
		logger.warn("===========================================================");
		
		String s3Key = pdfOperationParameter.getS3Key();
		
		int error = OperationError.ERROR_SUCCESS;
		//因A2项目需求，没有登录的用户可以签名，但是不能保存签名模版
		String userId = pdfOperationParameter.getUserId();
		
		logger.warn("userid="+userId+",s3key="+s3Key+"\n");
		
		Gson gson = new Gson();
		JsonParser jsonParser = new JsonParser();
		if (!FXStringUtils.IsNullOrEmpty(userId)) {
			//保存signature
			if(!FXStringUtils.IsNullOrEmpty(signatureData)){
				JsonArray array = jsonParser.parse(signatureData).getAsJsonArray();
				List<Signature> sigListmp = new ArrayList<Signature>();
				for(int i = 0;i < array.size();i++){
					Signature sig = new Signature();
					JsonElement json1 = array.get(i);
					JsonObject js1 = json1.getAsJsonObject().getAsJsonObject("signature");
					String id = js1.get("id").getAsString();
					
					sig.setCreateDate(new Date());
					sig.setId(id);
					sig.setPath("");
					if(!"img".equals(js1.get("type").getAsString())){
						sig.setFc(js1.get("fc").getAsString());
						sig.setFn(js1.get("fn").getAsString());
						sig.setFs(js1.get("fs").getAsInt());
						sig.setB(Boolean.valueOf(js1.get("b").getAsString()));
						sig.setI(Boolean.valueOf(js1.get("i").getAsString()));
						sig.setUl(Boolean.valueOf(js1.get("ul").getAsString()));
						sig.setTxt(js1.get("txt").getAsString());
					}else{
						String path = js1.get("path").getAsString();
						path = path.substring(path.lastIndexOf(",") + 1);
						if (!FXFileUtils.writeDataToFile(this.cacheRootPath + "signature/" + id,
								Base64.decodeBase64(path.getBytes("UTF-8")), true, true)) {
							error = OperationError.ERROR_SAVE_SIGN_LIST;
							break;
						}
					}
					
					sig.setUserId(userId);
					sig.setH(js1.get("h").getAsFloat());
					sig.setType(js1.get("type").getAsString());
					sig.setW(js1.get("w").getAsFloat());
					sigListmp.add(sig);
				}
				
				if(error == OperationError.ERROR_SUCCESS &&	array.size() > 0){
					error = OperationError.ERROR_SAVE_SIGN_LIST;
				}
			}

			//删除siganture
			if(error == OperationError.ERROR_SUCCESS && 
					!FXStringUtils.IsNullOrEmpty(signatureDelData)){
				List<String> ids = new ArrayList<String>();
				JsonArray delarray = jsonParser.parse(signatureDelData).getAsJsonArray();
				for(int i = 0;i < delarray.size();i++){
					ids.add(delarray.get(i).getAsString());
					if (!FileUtils.deleteQuietly(new File(this.cacheRootPath + 
							"signature/" + delarray.get(i).getAsString()))) {
						error = OperationError.ERROR_SAVE_SIGN_LIST;
						break;
					}
				}
				
				if (error == OperationError.ERROR_SUCCESS && !ids.isEmpty() ) {
					error = OperationError.ERROR_DELETE_SIGN_LIST;
				}
			}
		}
		
		if(error == OperationError.ERROR_SUCCESS && 
				!FXStringUtils.IsNullOrEmpty(signatureDocData)){
			//保存signatureDoc
			com.foxitcloud.webpdf.common.Signature sigt = new com.foxitcloud.webpdf.common.Signature();
			JsonArray arrayDoc = jsonParser.parse(signatureDocData).getAsJsonArray();
			for(int k = 0;k < arrayDoc.size();k++){
				JsonObject jt = arrayDoc.get(k).getAsJsonObject();
				String index = String.valueOf(jt.get("index").getAsInt());
				Map<String, Object> jm = gson.fromJson(jt.get("signatures"), new TypeToken<Map<String, Object>>(){}.getType());
				for(String key: jm.keySet()){//遍历JSONObject  
					//Modify by veikin at 2015-2-2,Can not forced to convert a map object to a json Object.
					String subJsonData = gson.toJson(jm.get(key));	
				    JsonObject jk = jsonParser.parse(subJsonData).getAsJsonObject();
					//JsonObject jk = (JsonObject) jm.get(key);
					//end here
					JsonObject jk1 = jk.get("signature").getAsJsonObject();
					logger.warn("signuature=  "+jk1.toString()+"\n");
					
					if(!"img".equals(jk1.get("type").getAsString())){//text
						TextSignatureData tsd = new TextSignatureData();
						String color = jk1.get("fc").getAsString().substring(1);
						tsd.setColor(Long.parseLong(color));
						tsd.setFontSize(jk1.get("fs").getAsFloat());
						tsd.setSignText(jk1.get("txt").getAsString());
						tsd.setxPos(jk1.get("x").getAsFloat());
						tsd.setyPos(jk1.get("y").getAsFloat());
						//统一设置为宋体
						tsd.setFontName("simsun");
						//tsd.setFontName(jk1.getString("fn"));
						
						sigt.addSignatureData(Integer.parseInt(index), tsd);
					}else{//img
						ImgSignatureData isd = new ImgSignatureData();
						isd.setxPos(jk1.get("x").getAsFloat());
						isd.setyPos(jk1.get("y").getAsFloat());
						isd.setZoom(jk1.get("scale").getAsFloat());
						byte[] buffer = null;
							String p = jk1.get("path").getAsString();
							p = p.substring(p.lastIndexOf(",") + 1);
							buffer = Base64.decodeBase64(p.getBytes("utf-8"));
							isd.setBuffer(buffer);
							isd.setSize(buffer.length);
						sigt.addSignatureData(Integer.parseInt(index), isd);
					}
				}				
				operationData.setSignatureData(gson.toJson(sigt).getBytes("UTF-8"));	
			}
		}
		logger.warn("1111111111111111111111111111111110\n");
		if(error == OperationError.ERROR_SUCCESS)
		{
			if (isFormMode && !FXStringUtils.IsNullOrEmpty(formXmlData)) {
				operationData.setFromData(0, formXmlData.getBytes("UTF-8"));
			}
			
			if(!FXStringUtils.IsNullOrEmpty(annotData)){
				operationData.setAnnotData(annotData);
			}
					
			ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
			ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
			objectOutputStream.writeObject(operationData);
			objectOutputStream.flush();
			objectOutputStream.close();
			cacheStoreProvider.addItemToCache(s3Key, 
					"operation/operationInfo", byteArrayOutputStream.toByteArray(), "", false);
			
			HashMap<String, Object> inputMap = new HashMap<String, Object>();
	        inputMap.put("s3Key", s3Key);
	        inputMap.put("type", pdfOperationParameter.getType());
	        inputMap.put("formMode", isFormMode);
	        String pdfPassword = "";
			try {
				pdfPassword = cacheStoreProvider.getPDFPassword(s3Key);
				if (pdfPassword.length() > 0) {
					pdfPassword = AESUtils.decrypt(
							pdfPassword,
							ConfigConstans.FOXIT_DOC_PASSWORD_DEFAULTPASS);
				}
			} catch (CacheException e) {
				logger.warn("(operation pdf) get doc password fail, s3key:" + s3Key);
				error = OperationError.ERROR_UNKNOWN;
			}
			
			if (error == OperationError.ERROR_SUCCESS) {
				inputMap.put("pdfPassword", pdfPassword);
		        inputMap.put("operationInfoPath", "operation/info" + String.valueOf(System.currentTimeMillis()));	      
				
			}
		}				
		
		retMap.put("error", error);
		return retMap;
	}
	
	private void addRequiredFieldXmlInfoToXmlDoc(Document docPages, String xmlInfo,
			int pageIndex) throws DocumentException {
		if (docPages == null || FXStringUtils.IsNullOrEmpty(xmlInfo))
			return;
		Element rootElement = docPages.getRootElement();
		Document xmlDocPage = DocumentHelper.parseText(xmlInfo);
		if (xmlDocPage != null) {
			Element pageElement = DocumentHelper.createElement("Page");
			Element formElement = xmlDocPage.getRootElement().element("Form");
			if (formElement == null) {
				return;
			} 
			List widgetElementList = formElement.elements("Widget");
			for (Iterator it = widgetElementList.iterator(); it.hasNext();) { 
		    	Element widgetElm = (Element)it.next();
		    	Element generalElement = (Element) widgetElm.element("General");
		    	if (generalElement != null) {
		    		Element requiredElement = (Element) generalElement.element("Required");
		    		if (requiredElement != null && "1".endsWith(requiredElement.getText())) {
			    		Element newWidgetElm = DocumentHelper.createElement("Widget");
			    		newWidgetElm.addAttribute("Name", widgetElm.attributeValue("Name"));
			    		newWidgetElm.addAttribute("Type", widgetElm.attributeValue("Type"));
			    		pageElement.add(newWidgetElm);
			    	}
		    	}
		    }
			
		    if (!pageElement.elements().isEmpty()) {
				pageElement.addAttribute("PageNo", String.valueOf(pageIndex));
				rootElement.add(pageElement);
		    }
		}
	}
	
	
	private void addXmlInfoToXmlDoc(Document docPages, String xmlInfo,
			int pageIndex) throws DocumentException {
		if (docPages == null)
			return;
		Element rootElement = docPages.getRootElement();
		Document xmlDocPage = DocumentHelper.parseText(xmlInfo);
		if (xmlDocPage != null) {
			Element pageElement = (Element) xmlDocPage.getRootElement().clone();
			pageElement.addAttribute("PageNo", String.valueOf(pageIndex));
			rootElement.add(pageElement);
		}
	}
	
	private void setErrorResponse(HttpServletResponse httpResponse, int serverState, int error)
			throws IOException {
		String errInfo = "{\"error\":" + String.valueOf(error) + ",\"status\":"
				+ String.valueOf(serverState) + "}";
		httpResponse.setHeader("Pragma", "no-cache");
		httpResponse.setDateHeader("Expires", 0);
		httpResponse.addHeader("Cache-Control", "no-cache");
		httpResponse.addHeader("Cache-Control", "no-store");
		httpResponse.addHeader("Cache-Control", "must-revalidate");
		Date now = new Date();
		httpResponse.addHeader("ETag", String.valueOf(now.getTime()));
		httpResponse.getWriter().print(errInfo);
		httpResponse.setStatus(HttpServletResponse.SC_OK);
	}
	
	private boolean isManifestDataAccessed(HttpServletResponse response, String s3Key) {
		/*if (!iWebReaderStoreProvider.isManifestDataAccessed(s3Key)) {
			try {
				String pdfPasswordFromCache = cacheStoreProvider
						.getPDFPassword(s3Key);
				if(pdfPasswordFromCache != null || pdfPasswordFromCache.length() > 0){
					logger.warn("Unauthorized request for annotation data.");
					response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
					return false;
				}
			} catch (CacheException e) {
				logger.warn(e.getMessage());
				logger.warn("Check manifest data accessed failed, id:"
						+ s3Key);
				response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
				return false;
			}
		}*/
		return true;
	}
	
	private String checkNoneMathch(HttpServletResponse response, HttpServletRequest request, String s3Key, String description) {
		String etag = s3Key + "_" + description;
		String strIfNoneMatch = request.getHeader("If-None-Match");
		if (etag.equals(strIfNoneMatch)) {
			response.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
			return null;
		}
		return etag;
	}
	
	private boolean readerPage(String s3Key, boolean formMode, int pageIndex, String pagesIndex) {
		HashMap<String, Object> inputMap = new HashMap<String, Object>();
        inputMap.put("s3Key", s3Key);
        inputMap.put("formMode", formMode);
        inputMap.put("pageIndex", pageIndex);
        if (pageIndex == -1) {
        	inputMap.put("pagesIndex", pagesIndex);
        }
        String pdfPasswordFromCache;
		try {
			pdfPasswordFromCache = cacheStoreProvider
					.getPDFPassword(s3Key);
		} catch (CacheException e) {
			logger.warn("(reader page) get doc password fail, s3key:" + s3Key);
			return false;
		}
		String pdfPassword = pdfPasswordFromCache;
		if (pdfPasswordFromCache.length() > 0) {
			pdfPassword = AESUtils.decrypt(
					pdfPasswordFromCache,
					ConfigConstans.FOXIT_DOC_PASSWORD_DEFAULTPASS);
		}
        inputMap.put("pdfPassword", pdfPassword);		
		return false;
	}
}
